#include "u_peer_manager.h"

#include <iostream>
using namespace std;

void UPeerManager::handle(const udp::endpoint& remoteAddr, IPackage::Ptr package, IPackageSender::Ptr packageHandler, unsigned int packageSeq)
{
	auto itr = map_.find(remoteAddr);
	if(itr == map_.end())
	{
		if(package)
		{
			handleNewPeerMessage(remoteAddr, package, packageHandler, packageSeq);
		}
		return;
	}
	auto peer = (*itr).second;
	if(!package)
	{
		peer->close();
		return;
	}
	if(peer->onPackageReceived(package, packageSeq))
	{
		package->handle(peer);
	}
}

void UPeerManager::handleNewPeerMessage(const udp::endpoint& remoteAddr, IPackage::Ptr package, IPackageSender::Ptr packageHandler, unsigned int packageSeq)
{
	auto peer = UPeer::Ptr(new UPeer(remoteAddr, packageHandler));

	if(peer->onPackageReceived(package, packageSeq))
	{
		package->handle(peer);
	}
	if(!peer->isDisposed())
	{
		cout << " new connection arrived, package id: " << package->messageID() << " from " <<
			remoteAddr.address().to_string() << ", port:" << remoteAddr.port() << endl;
		map_.insert(Map::value_type(remoteAddr, peer));
		peer->start();
	}
}

void UPeerManager::onConnectionEstablished(UPeer::Ptr peer, bool connectingSide)
{
	if(connectionListener_)
	{
		if(connectingSide)
		{
			connectionListener_->onConnectionEstablished(peer);
		}
		else
		{
			connectionListener_->onNewConnectionConnected(peer);
		}
	}
}

void UPeerManager::onConnectionClosed(const udp::endpoint& remoteAddr, UPeer::ShutdownReason reason)
{
	auto itr = map_.find(remoteAddr);
	if(itr != map_.end())
	{
		cout << "connection to " << remoteAddr.address().to_string() <<
			":" << remoteAddr.port() << " closed, reason:" << reason << endl;
		if(connectionListener_ && reason != UPeer::INPURPOSE)
		{
			connectionListener_->onConnectionLost((*itr).second);
		}
		map_.erase(itr);
	}
}

UPeer::Ptr UPeerManager::createChannelTo(const udp::endpoint& remoteAddr, IPackageSender::Ptr packageHandler)
{
	auto peer = UPeer::Ptr(new UPeer(remoteAddr, packageHandler));
	map_.insert(Map::value_type(remoteAddr, peer));
	peer->start();
	peer->sendConnectingPackage();
	return peer;
}

void UPeerManager::setConnectionListener(IConnectionListener::Ptr listener)
{
	connectionListener_ = listener;
}

SingletonSurpportImp(UPeerManager);
